#pragma once

enum EntryType
{
	TYPE_WITHOUT_DATA = 0x20000,

	TYPE_VALUE		= 0x0001,
	TYPE_LIST		= 0x0002,
	TYPE_CHECK		= 0x0004,
	TYPE_INFO		= 0x0008 | TYPE_WITHOUT_DATA,

	TYPE_A1_MASK	= 0x00f0,
	TYPE_MIN_I		= 0x0010,
	TYPE_MIN_L		= 0x0020,

	TYPE_A2_MASK	= 0x0f00,
	TYPE_MAX_I		= 0x0100,
	TYPE_MAX_L		= 0x0200,

	TYPE_CONST		= 0x1000,
	TYPE_HIDE		= 0x2000,
	TYPE_PROTOTYPE	= 0x4000 | TYPE_HIDE | TYPE_LIST,
	TYPE_PROTO_INFO	= TYPE_PROTOTYPE | TYPE_CONST,

	TYPE_PROTO_ENTRY = 0x8000,
	TYPE_PROTO_ENTRY_C = 0x10000 | TYPE_PROTO_ENTRY,

	TYPE_VALUE_RANGE_I_I = TYPE_VALUE | TYPE_MIN_I | TYPE_MAX_I,
	TYPE_VALUE_RANGE_I_R = TYPE_VALUE | TYPE_MIN_I | TYPE_MAX_L,
	TYPE_VALUE_RANGE_R_I = TYPE_VALUE | TYPE_MIN_L | TYPE_MAX_I,
	TYPE_VALUE_RANGE_R_R = TYPE_VALUE | TYPE_MIN_L | TYPE_MAX_L,
};

enum
{
	ARG_NONE = 0,
};

enum ConnectType
{
	CONNECT_NONE,
	CONNECT_BY_SEL,
	CONNECT_BY_VAL,
	CONNECT_SEL_AS_VAL,
	CONNECT_VAL_AS_SEL,
};

#define NULL_CONNECT ((MainTableEntry*)0)

struct MainTableEntryArgs
{
	u32 a1;
	u32 a2;
	u32 a3;
	u32 a4;
};

struct MainTableEntry
{
	MainTableEntry* parent;
	wxString name;
	wxWindow* gui;
	wxBoxSizer* sizer;
	MainTableEntry* connected;
	u32 offset;
	u8 size;
	s32 value;
	const u32 type;
	MainTableEntry* sublist;
	const u32 sublist_size;
	MainTableEntry* connect_table;
	ConnectType connect_type;

	MainTableEntryArgs args;
};

struct ValueTableEntry
{
	const u32 id;
	wxString name;
	MainTableEntry sublist_table;

	wxString GetName() const
	{
		wxString ret = name;

		ret.Replace("%idh", wxString::Format("0x%x", id));
		ret.Replace("%id", wxString::Format("%d", id));

		return ret;
	}
};

struct OffsetListEntry
{
	const u32 read_offset;
	const u32 value;
	const u32 size;
	const u32 offset;

	s32 (*GetOffset)(const void* data);
};

struct TableListEntry
{
	MainTableEntry* table;
	const u32 table_size;
	const OffsetListEntry* offset;
	const u32 offset_size;
	s32 cur_offset;
};

struct ListEntryArgs
{
	ValueTableEntry* values;
	u32 values_count;
	u32 : 32;
	u32 : 32;
};

struct ValueEntryArgs
{
	union
	{
		u32 put[2];

		struct
		{
			MainTableEntry* min_connect_table;
			MainTableEntry* max_connect_table;
		};

		struct
		{
			u32 min_connect_imm;
			u32 max_connect_imm;
		};
	};

	u32 : 32;
	u32 : 32;
};

struct CheckEntryArgs
{
	u32 check_1;
	u32 check_2;
	u32 check_3;
	u32 enable_check_3;
};

struct InfoEntryArgs
{
	ValueTableEntry* list;
	u32 list_count;
	ValueTableEntry* proto;
	u32 proto_count;
};

#define VALUE_ENTRY_LC(i, n, offs, size, l, connect_table, connect_type, ...) \
	{i, n, {0, "", 0, 0, 0, offs, size, 0, TYPE_LIST __VA_ARGS__, 0, 0, &connect_table, connect_type, {(u32)l, WXSIZEOF(l), ARG_NONE, ARG_NONE}}}

#define VALUE_ENTRY_IC(n, l, ...) \
	VALUE_ENTRY_LC(0, n, 0, 0, l, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define VALUE_ENTRY_L(i, n, offs, size, l, ...) \
	VALUE_ENTRY_LC(i, n, offs, size, l, *NULL_CONNECT, 0, __VA_ARGS__)

#define VALUE_ENTRY_R(i, n, ...) \
	{i, n, {0, "", 0, 0, 0, 0, 0, 0, /*type*/0 __VA_ARGS__, 0, 0, NULL_CONNECT, CONNECT_NONE, {ARG_NONE, ARG_NONE, ARG_NONE, ARG_NONE}}}

#define VALUE_ENTRY_PI(i, ...) VALUE_ENTRY_R(i, "", __VA_ARGS__)
#define VALUE_ENTRY_PNF(n, f, ...) \
	{0, n, {0, "", 0, 0, 0, 0, 0, 0, 0 __VA_ARGS__, 0, 0, NULL_CONNECT, CONNECT_NONE, {(u32)f, ARG_NONE, ARG_NONE, ARG_NONE}}}

#define VALUE_ENTRY_PN(n, ...) VALUE_ENTRY_R(0, n, __VA_ARGS__)


#define MAIN_ENTRY_RSC(name, offset, size, type, sublist, connect_table, connect_type, ...) \
	{0, name, 0, 0, 0, offset, size, -1, type, sublist, WXSIZEOF(sublist), &connect_table, connect_type, __VA_ARGS__}

#define MAIN_ENTRY_RS(name, offset, size, type, sublist, ...) \
	MAIN_ENTRY_RSC(name, offset, size, type, sublist, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_RC(name, offset, size, type, connect_table, connect_type, ...) \
	{0, name, 0, 0, 0, offset, size, -1, type, 0, 0, &connect_table, connect_type, __VA_ARGS__}

#define MAIN_ENTRY_R(name, offset, size, type, ...) \
	MAIN_ENTRY_RC(name, offset, size, type, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_IC(table, connect_table, connect_type, ...) \
	{0, "", 0, 0, 0, 0, 0, -1, TYPE_PROTO_INFO __VA_ARGS__, table, WXSIZEOF(table), &connect_table, connect_type, {ARG_NONE, ARG_NONE, ARG_NONE, ARG_NONE}}

#define MAIN_ENTRY_I(table, ...) \
	MAIN_ENTRY_IC(table, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_PC(name, offset, size, table, proto, connect_table, connect_type, ...) \
	{0, name, 0, 0, 0, offset, size, -1, TYPE_PROTOTYPE __VA_ARGS__, table, WXSIZEOF(table), &connect_table, connect_type, {(u32)proto, WXSIZEOF(proto), ARG_NONE, ARG_NONE}}

#define MAIN_ENTRY_P(name, offset, size, table, proto, ...) \
	MAIN_ENTRY_PC(name, offset, size, table, proto, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_LSC(name, offset, size, tname, sublist, connect_table, connect_type, ...) \
	MAIN_ENTRY_RSC(name, offset, size, TYPE_LIST __VA_ARGS__, sublist, connect_table, connect_type, (u32)tname, WXSIZEOF(tname), ARG_NONE, ARG_NONE)

#define MAIN_ENTRY_LC(name, offset, size, tname, connect_table, connect_type, ...) \
	MAIN_ENTRY_RC(name, offset, size, TYPE_LIST __VA_ARGS__, connect_table, connect_type, (u32)tname, WXSIZEOF(tname), ARG_NONE, ARG_NONE)

#define MAIN_ENTRY_C3C(name, offset, size, connect_table, connect_type, a1, a2, a3, ...) \
	MAIN_ENTRY_RC(name, offset, size, TYPE_CHECK __VA_ARGS__, connect_table, connect_type, a1, a2, a3, 1)

#define MAIN_ENTRY_C3(name, offset, size, a1, a2, a3, ...) \
	MAIN_ENTRY_C3C(name, offset, size, *NULL_CONNECT, CONNECT_NONE, a1, a2, a3, __VA_ARGS__)

#define MAIN_ENTRY_CC(name, offset, size, connect_table, connect_type, a1, a2, ...) \
	MAIN_ENTRY_RC(name, offset, size, TYPE_CHECK __VA_ARGS__, connect_table, connect_type, a1, a2, 0, 0)

#define MAIN_ENTRY_C(name, offset, size, a1, a2, ...) \
	MAIN_ENTRY_CC(name, offset, size, *NULL_CONNECT, CONNECT_NONE, a1, a2, __VA_ARGS__)

#define MAIN_ENTRY_LS(name, offset, size, tname, sublist, ...) \
	MAIN_ENTRY_LSC(name, offset, size, tname, sublist, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_L(name, offset, size, tname, ...) \
	MAIN_ENTRY_LC(name, offset, size, tname, *NULL_CONNECT, CONNECT_NONE, __VA_ARGS__)

#define MAIN_ENTRY_LI(name, offset, size, list, info, proto, ...) \
	MAIN_ENTRY_RS(name, offset, size, TYPE_LIST | TYPE_INFO __VA_ARGS__, info, (u32)list, WXSIZEOF(list), (u32)proto, WXSIZEOF(proto))

#define MAIN_ENTRY_PL(l, ...) MAIN_ENTRY_L("", 0, 0, l, | TYPE_PROTO_ENTRY __VA_ARGS__)
#define MAIN_ENTRY_PLC(l, ...) MAIN_ENTRY_PL(l, | TYPE_PROTO_ENTRY_C __VA_ARGS__)

#define LIST_ENTRY_R(t) {t, WXSIZEOF(t), 0, 0, -1}
#define LIST_ENTRY_L(t, l) {t, WXSIZEOF(t), l, WXSIZEOF(l), -1}

#define OFFSET_ENTRY_V(lo, v, t, o) {lo, v, sizeof(t), o, 0} //if(stream[lo : sizeof(t)] == v) seek(o); | sizeof(t) < 4
#define OFFSET_ENTRY_F(lo, s, f) {lo, 0, s, 0, f} //o = f(stream[lo : s])); if(o >= 0) seek(o);

class TableGUI
{
protected:
	TableGUI();
	void Init(TableListEntry* table_list, u32 table_list_size);

	void Save(int preoffset, MainTableEntry& table);
	bool Load(int preoffset, MainTableEntry* table, int table_num, bool check = false);

	virtual bool Open(const wxString&) = 0;

	virtual void Read(u32 addr, void* dst, u32 size)=0;
	virtual void Write(u32 addr, const void* src, u32 size)=0;

public:
	wxBoxSizer* GetBoxSizer(wxWindow* parent);

	virtual void Close() = 0;

	void Save();
	bool Load();
};

class FileTableGUI : public TableGUI
{
	wxFile f;

public:
	virtual bool Test(const wxString& path)=0;
	bool Open(const wxString& path);
	void Close();

	void Read(u32 offset, void* dst, u32 size);
	void Write(u32 offset, const void* src, u32 size);
};

#include "Process.h"

class ProcessTableGUI : public TableGUI
{
	ProcessManager m_proc_manager;

public:
	virtual bool Test(const wxString& process)=0;
	bool Open(const wxString& process);
	void Close();

	void Read(u32 offset, void* dst, u32 size);
	void Write(u32 offset, const void* src, u32 size);
};